#pragma once

#include "qlib/layout/cell.h"

#include <typeindex>

#include <tuple>
#include <variant>

namespace qlib {

// cell中几何元素类型
#define QLIB_ELEMENT_TYPE \
  qlib::Polygon, qlib::Path, qlib::Rectangle, qlib::Circle, qlib::Ellipse, qlib::Fan, qlib::Label, qlib::Ruler, qlib::KeyPoint, qlib::Pin

class CellUtils {
 public:
  static void copy_registry(const entt::registry& old_reg, entt::registry& new_reg) { copy_registry_imp<QLIB_CELL_ELEMENT_TYPE>(old_reg, new_reg); }

 private:
  template <typename T, typename... Args>
  static void copy_registry_imp(const entt::registry& old_reg, entt::registry& new_reg) {
    auto view = old_reg.view<std::shared_ptr<T>>();

    view.each([&new_reg](const std::shared_ptr<T>& ptr) {
      auto new_ptr = ptr->copy();
      auto new_entity = new_reg.create();
      new_reg.emplace_or_replace<std::shared_ptr<T>>(new_entity, new_ptr);

      if constexpr (!std::is_same_v<T, qlib::Reference>) {
        qlib::Tag tag = qlib::Tag(ptr->layer, ptr->datatype);
        new_reg.emplace_or_replace<qlib::Tag>(new_entity, tag);
      } else {
      }
    });

    if constexpr (sizeof...(Args) > 0) {
      copy_registry_imp<Args...>(old_reg, new_reg);
    }
  }
};

using CellElement = std::variant<std::nullptr_t, qlib::Path::s_ptr, qlib::Polygon::s_ptr, qlib::Rectangle::s_ptr, qlib::Label::s_ptr,
                                 qlib::Text::s_ptr, qlib::Ellipse::s_ptr, qlib::Fan::s_ptr, qlib::Ruler::s_ptr, qlib::Reference::s_ptr>;

struct CellElements {
  // entt::registry &reg;
  CellElements();
  CellElements(std::shared_ptr<Cell> cell);
  CellElements(entt::registry& reg);

  std::vector<std::tuple<entt::entity, qlib::Path::s_ptr>> paths;
  std::vector<std::tuple<entt::entity, qlib::Polygon::s_ptr>> polygons;
  std::vector<std::tuple<entt::entity, qlib::Rectangle::s_ptr>> rectangles;
  std::vector<std::tuple<entt::entity, qlib::Label::s_ptr>> labels;
  std::vector<std::tuple<entt::entity, qlib::Text::s_ptr>> texts;
  std::vector<std::tuple<entt::entity, qlib::Ellipse::s_ptr>> ellipses;
  std::vector<std::tuple<entt::entity, qlib::Fan::s_ptr>> fans;
  std::vector<std::tuple<entt::entity, qlib::Ruler::s_ptr>> rulers;
  std::vector<std::tuple<entt::entity, qlib::Reference::s_ptr>> references;

  template <typename TagType>
  void initHaveTagElements(entt::registry& reg) {
    {
      auto view = reg.view<qlib::Path::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Path::s_ptr ptr) { paths.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Polygon::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Polygon::s_ptr ptr) { polygons.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Rectangle::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Rectangle::s_ptr ptr) { rectangles.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Label::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Label::s_ptr ptr) { labels.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Text::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Text::s_ptr ptr) { texts.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Ellipse::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Ellipse::s_ptr ptr) { ellipses.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Fan::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Fan::s_ptr ptr) { fans.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Ruler::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Ruler::s_ptr ptr) { rulers.push_back({e, ptr}); });
    }

    {
      auto view = reg.view<qlib::Reference::s_ptr, TagType>();
      view.each([&](entt::entity e, qlib::Reference::s_ptr ptr) { references.push_back({e, ptr}); });
    }
  }
};

bool entity_valid(entt::registry& reg, entt::entity e);

}  // namespace qlib